#include "util.h"
#include <stdint.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>

void (*signal_intr(int signo, void (*sighandler)(int)))(int)
{
    struct sigaction sa_new;
    struct sigaction sa_old;

    sa_new.sa_handler = sighandler;
    sigemptyset(&sa_new.sa_mask);
    sa_new.sa_flags = 0; 
#ifdef SA_INTERRUPT
    sa_new.sa_flags |= SA_INTERRUPT;
#endif

    if (sigaction(signo, &sa_new, &sa_old) != 0)
    {
        return SIG_ERR;
    }
    else
    {
        return sa_old.sa_handler;
    }
}

void hexdump(const void *input, const size_t length)
{
    size_t i = 0;

    for (i = 0; i < length; ++i)
    {
        printf("%02hhX ", ((char *)input)[i]);
    }
    printf("\n");
}

void err_exit(const char *format, ...)
{
    va_list va;
    va_start(va, format);
    vfprintf(stderr, format, va);
    fprintf(stderr, "\nerror=[%d][%s]\n", errno, strerror(errno));
    va_end(va);
    exit(1);
}

ssize_t read_n(const int fd, void *buff, const size_t buff_len)
{
    ssize_t nread = 0;
    size_t nleft = buff_len;
    char *ptr = buff; 

    while (nleft > 0)
    {
        if ((nread = read(fd, ptr, nleft)) < 0)
        {
            if (EINTR == errno)
            {
                continue;
            }
            else
            {
                return -1;
            }
        }
        else if (nread == 0)
        {
            break;
        }
        nleft -= nread;
        ptr += nread;
    }
    return buff_len - nleft;
}

ssize_t write_n(const int fd, const void *buff, const size_t buff_len)
{
    ssize_t nwritten = 0;
    size_t nleft = buff_len;
    const char *ptr = buff; 

    while (nleft > 0)
    {
        if ((nwritten = write(fd, ptr, nleft)) < 0)
        {
            if (EINTR == errno)
            {
                continue;
            }
            else
            {
                return -1;
            }
        }
        else if (0 == nwritten)
        {
            return -1;
        }

        nleft -= nwritten;
        ptr += nwritten;
    }

    return buff_len;
}

int put_string(const int fd, const char *buff, const size_t max_len)
{
    uint32_t packlen_host = (uint32_t)(MIN(strlen(buff), max_len));
    uint32_t packlen_net = htonl(packlen_host);

    if (write_n(fd, &packlen_net, sizeof(uint32_t)) < 0)
    {
        return -1;
    }

    if (write_n(fd, buff, packlen_host) < 0)
    {
        return -1;
    }

    return packlen_host;
}

int get_string(const int fd, char *buff, const size_t max_len)
{
    uint32_t packlen_host;
    uint32_t packlen_net;
    ssize_t nread;
    char buff_skip[1024];
    ssize_t nskip= 0;

    if ((nread = read_n(fd, &packlen_net, sizeof(uint32_t))) <= 0)
    {
        return nread;
    }

    packlen_host = ntohl(packlen_net);
    if (max_len < packlen_host)
    {
        nskip = packlen_host - max_len;
    }

    if ((nread = read_n(fd, buff, packlen_host - nskip)) <= 0)
    {
        return nread;
    }

    while (nskip > 0)
    {
        if ((nread = read_n(fd, buff_skip, MIN(nskip, sizeof(buff_skip)))) <= 0)
        {
            return nread;
        }
        nskip -= nread;
    }

    return packlen_host;
}
